package fr.jade.fraclite.orb;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.logging.Logger;

import org.jgroups.Channel;
import org.jgroups.JChannel;
import org.jgroups.JChannelFactory;
import org.jgroups.blocks.GroupRequest;
import org.jgroups.util.RspList;
import org.objectweb.fractal.api.Interface;

import fr.jade.fraclite.orb.api.Name;
import fr.jade.fraclite.orb.api.RemoteReference;
import fr.jade.fraclite.orb.io.FHAMarshaller;
import fr.jade.fraclite.orb.jgroups.RpcDispatcher;
import fr.jade.fraclite.orb.names.JGroupsName;

/**
 * @author Sylvain Sicard
 * @author <a href="mailto:florent.metral@inrialpes.fr">Florent Metral
 *
 */
public class MulticastStub implements InvocationHandler, RemoteReference {
  protected Logger log = Logger.getLogger(getClass().getName());
  protected JChannelFactory factoryStub = null;
  protected Channel channel = null;
  protected RpcDispatcher disp = null;
  protected JGroupsName id = null;
	protected static Integer stubCounter = 0;
	protected String stubId;
	protected Integer callCounter;
	protected Name name;

	public MulticastStub(Name name, String stubId) {
	  //stubId = stubCounter++;
	  this.stubId = stubId;
	  callCounter = 0;
	  this.name = name;
	  this.factoryStub = new JChannelFactory();
	  try {
      factoryStub.setMultiplexerConfig("stacks.xml");
    } catch (Exception e1) {
      e1.printStackTrace();
    }
	  this.id = (JGroupsName)name;
		try {
			//channel = factoryStub.createMultiplexerChannel("udp", Long.toString(id.getItfId()));
			channel = factoryStub.createChannel("udp");
			channel.setOpt(Channel.LOCAL, false);

			disp=new RpcDispatcher(channel, null, null, null);
			FHAMarshaller m = new FHAMarshaller(name.getNaming());
			disp.setMarshaller(m);
			disp.setResponseMarshaller(m);
			disp.setRequestMarshaller(m);

			log.fine("[stub] connect "+id.getGroupId());
			channel.connect(id.getGroupId());
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
		RspList rsp_list = null;
		System.out.println("Call " + m.getName() + " on " + id.getGroupId());
		try{
			MulticastStub.class.getMethod(m.getName(), m.getParameterTypes());
			Object res =  m.invoke(this, args);
			return res;
		}catch(NoSuchMethodException e){
			rsp_list=disp.callRemoteMethods(stubId+"@"+callCounter++,null, m.getName(), args, m.getParameterTypes(), GroupRequest.GET_ALL, 10000);	
			Object res;
			if(!rsp_list.getResults().isEmpty()){
				res = rsp_list.getResults().firstElement();
			}else{
				res = null;
			}
			if(res instanceof Exception){
//				((Exception)rsp_list.getFirst()).printStackTrace();
				throw (Exception)res;
			}
			else{
				System.out.println("Responses: " + rsp_list);
				return res;
			}
		}
	}

	@Override
	public boolean equals(Object o){
	  if (o instanceof Interface) {
      return id.getItfId()==((Interface)o).getFcItfGUId();
    }else{
      return false;
    }
	}

  @Override
  public Name getName() {
    return name;
  }

}